# -*- coding: utf-8 -*-
"""
@author:xuyuntao
@time:2021/2/10:10:17
@email:xuyuntao@189.cn
"""
"""计算各信号的高阶累积量，需要输入基带信号
所有输入constel参数均假设一行为一个信号的采样序列，可以有多行，也可以只有一行
末尾带_avg的函数针对同时输入多个信号的情况，求其平均值"""
import numpy

def M20(constel):
    oneDim=False
    if len(constel.shape)==1:
        oneDim=True
        constel=constel.reshape([1,-1])
    value=numpy.sum(constel ** 2,axis=1) / constel.shape[1]
    if oneDim:
        value=value[0]
    return value

def M21(constel):
    oneDim=False
    if len(constel.shape)==1:
        oneDim=True
        constel=constel.reshape([1,-1])
    value=numpy.sum(numpy.abs(constel) ** 2,axis=1) / constel.shape[1]
    if oneDim:
        value=value[0]
    return value

def M22(constel):
    oneDim=False
    if len(constel.shape)==1:
        oneDim=True
        constel=constel.reshape([1,-1])
    value=numpy.sum(constel.conjugate() ** 2,axis=1) / constel.shape[1]
    if oneDim:
        value=value[0]
    return value

def M40(constel):
    oneDim=False
    if len(constel.shape)==1:
        oneDim=True
        constel=constel.reshape([1,-1])
    value=numpy.sum(constel ** 4,axis=1) / constel.shape[1]
    if oneDim:
        value=value[0]
    return value

def M41(constel):
    oneDim=False
    if len(constel.shape)==1:
        oneDim=True
        constel=constel.reshape([1,-1])
    value=numpy.sum(numpy.abs(constel) ** 2 * constel ** 2,axis=1) / constel.shape[1]
    if oneDim:
        value=value[0]
    return value

def M42(constel):
    oneDim=False
    if len(constel.shape)==1:
        oneDim=True
        constel=constel.reshape([1,-1])
    value=numpy.sum(numpy.abs(constel) ** 4,axis=1) / constel.shape[1]
    if oneDim:
        value=value[0]
    return value

def M43(constel):
    oneDim=False
    if len(constel.shape)==1:
        oneDim=True
        constel=constel.reshape([1,-1])
    value=numpy.sum(numpy.abs(constel) ** 2 * constel.conjugate() ** 2,axis=1) / constel.shape[1]
    if oneDim:
        value=value[0]
    return value

def M60(constel):
    oneDim=False
    if len(constel.shape)==1:
        oneDim=True
        constel=constel.reshape([1,-1])
    value=numpy.sum(constel ** 6,axis=1) / constel.shape[1]
    if oneDim:
        value=value[0]
    return value

def M61(constel):
    oneDim=False
    if len(constel.shape)==1:
        oneDim=True
        constel=constel.reshape([1,-1])
    value=numpy.sum(numpy.abs(constel) ** 2 * constel ** 4,axis=1) / constel.shape[1]
    if oneDim:
        value=value[0]
    return value

def M62(constel):
    oneDim=False
    if len(constel.shape)==1:
        oneDim=True
        constel=constel.reshape([1,-1])
    value=numpy.sum(numpy.abs(constel) ** 4 * constel ** 2,axis=1) / constel.shape[1]
    if oneDim:
        value=value[0]
    return value

def M63(constel):
    oneDim=False
    if len(constel.shape)==1:
        oneDim=True
        constel=constel.reshape([1,-1])
    value=numpy.sum(numpy.abs(constel) ** 6,axis=1) / constel.shape[1]
    if oneDim:
        value=value[0]
    return value

def C20(constel):
    oneDim=False
    if len(constel.shape)==1:
        oneDim=True
        constel=constel.reshape([1,-1])
    value=M20(constel)
    if oneDim:
        value=value[0]
    return value

def C21(constel):
    oneDim=False
    if len(constel.shape)==1:
        oneDim=True
        constel=constel.reshape([1,-1])
    value=M21(constel)
    if oneDim:
        value=value[0]
    return value

def C40(constel):
    oneDim=False
    if len(constel.shape)==1:
        oneDim=True
        constel=constel.reshape([1,-1])
    value= M40(constel) - 3 * M20(constel) ** 2
    if oneDim:
        value=value[0]
    return value

def C41(constel):
    oneDim=False
    if len(constel.shape)==1:
        oneDim=True
        constel=constel.reshape([1,-1])
    value= M41(constel) - 3 * M20(constel) * M21(constel)
    if oneDim:
        value=value[0]
    return value

def C42(constel):
    oneDim=False
    if len(constel.shape)==1:
        oneDim=True
        constel=constel.reshape([1,-1])
    value= M42(constel) - numpy.abs(M20(constel)) ** 2 - \
           2 * (M21(constel) ** 2)
    if oneDim:
        value=value[0]
    return value

def C60(constel):
    oneDim=False
    if len(constel.shape)==1:
        oneDim=True
        constel=constel.reshape([1,-1])
    value= M60(constel) - 15 * M20(constel) * M40(constel) + \
           3 * M20(constel) ** 3
    if oneDim:
        value=value[0]
    return value

def C61(constel):
    oneDim=False
    if len(constel.shape)==1:
        oneDim=True
        constel=constel.reshape([1,-1])
    value= M61(constel) - 5 * M21(constel) * M40(constel) - \
           10 * M20(constel) * M41(constel) + 30 * M20(constel) ** 2 * M21(constel)
    if oneDim:
        value=value[0]
    return value

def C62(constel):
    oneDim=False
    if len(constel.shape)==1:
        oneDim=True
        constel=constel.reshape([1,-1])
    value= M62(constel) - 6 * M20(constel) * M42(constel) - \
           8 * M21(constel) * M41(constel) - \
           M22(constel) * M40(constel) + 6 * M20(constel) ** 2 * M22(constel) + \
           24 * M21(constel) ** 2 * M20(constel)
    if oneDim:
        value=value[0]
    return value

def C63(constel):
    oneDim=False
    if len(constel.shape)==1:
        oneDim=True
        constel=constel.reshape([1,-1])
    value= M63(constel) - 9 * M21(constel) * M42(constel) + \
           12 * M21(constel) ** 3 - 3 * M20(constel) * M43(constel) - \
           3 * M22(constel) * M41(constel) + \
           18 * M20(constel) * M21(constel) * M22(constel)
    if oneDim:
        value=value[0]
    return value

highCumMethods=[C20, C21, C40, C41, C42, C60, C61, C62, C63]

def C20_avg(constel):
    return (M20(constel)).mean()
def C21_avg(constel):
    return (M21(constel)).mean()
def C40_avg(constel):
    return (M40(constel) - 3 * M20(constel) ** 2).mean()
def C41_avg(constel):
    return (M41(constel) - 3 * M20(constel) * M21(constel)).mean()
def C42_avg(constel):
    return (M42(constel) - numpy.abs(M20(constel)) ** 2 -
            2 * (M21(constel) ** 2)).mean()
def C60_avg(constel):
    return (M60(constel) - 15 * M20(constel) * M40(constel) +
            3 * M20(constel) ** 3).mean()
def C61_avg(constel):
    return (M61(constel) - 5 * M21(constel) * M40(constel) -
            10 * M20(constel) * M41(constel) +
            30 * M20(constel) ** 2 * M21(constel)).mean()
def C62_avg(constel):
    return (M62(constel) - 6 * M20(constel) * M42(constel) -
            8 * M21(constel) * M41(constel) - M22(constel) *
            M40(constel) + 6 * M20(constel) ** 2 * M22(constel) +
            24 * M21(constel) ** 2 * M20(constel)).mean()
def C63_avg(constel):
    return (M63(constel) - 9 * M21(constel) * M42(constel) +
            12 * M21(constel) ** 3 - 3 * M20(constel) * M43(constel) -
            3 * M22(constel) * M41(constel) +
            18 * M20(constel) * M21(constel) * M22(constel)).mean()

def f_x1(constel):
    return (C63(constel) ** 2) / (C42(constel) ** 3)

def f_x2(constel):
    return C40(constel) / C42(constel)

def f_x3(constel):
    return C41(constel) / C42(constel)

def f_x1_avg(constel):
    return ((C63(constel) ** 2) / (C42(constel) ** 3)).mean()

def f_x2_avg(constel):
    return (C40(constel) / C42(constel)).mean()

def f_x3_avg(constel):
    return (C41(constel) / C42(constel)).mean()

def c41c202(constel):
    return ((C41(constel)) / (C20(constel) ** 2)).mean()